﻿#include "timer_wheel.hh"
#include "../box/service_box.hh"
#include "../detail/lang_impl.hh"
#include "../util/string_util.hh"
#include <atomic>
#include <chrono>
#include <mutex>

namespace kratos {
namespace util {

std::size_t global_wheel_timer_count = 0;

/**
 * 链表节点.
 */
struct WheelNode {
  WheelNode *prev{nullptr}; ///< 上一个节点
  WheelNode *next{nullptr}; ///< 下一个节点
  std::uint64_t data{0};    ///< 用户数据
  std::time_t duration{0};  ///< 延迟时间，毫秒
  std::time_t deadline{0};  ///< 超时时间戳，毫秒
  bool is_repeated{false};  ///< 是否是重复定时器
  TimerList *list{nullptr}; ///< 所属槽位链表
  Handler handler{nullptr}; ///< 定时器处理器
  bool cancelled{false};    ///< 是否已经取消
};

/**
 * 双向循环链表.
 */
struct TimerList {
  WheelNode *head{nullptr};    ///< 链表头
  std::atomic_size_t count{0}; ///< 链表内节点数量
  TimerWheel *wheel{nullptr};  ///< 时间轮
  /**
   * 构造.
   *
   * \param timer_wheel 时间轮
   */
  TimerList(TimerWheel *timer_wheel);
  /**
   * 析构.
   *
   */
  ~TimerList();
  /**
   * 链表头添加节点.
   *
   * \param node 节点
   */
  void add_front(WheelNode *node);
  /**
   * 链表尾添加节点.
   *
   * \param node 节点
   */
  void add_tail(WheelNode *node);
  /**
   * 将节点从链表内移出.
   *
   * \param node 节点
   * \return 节点
   */
  WheelNode *remove(WheelNode *node);
  /**
   * 销毁节点.
   *
   * \param node 节点
   */
  void destroy(WheelNode *node);
  /**
   * 获取链表的第一个节点.
   *
   * \return 第一个节点
   */
  WheelNode *first_node();
  /**
   * 当前节点的下一个节点.
   *
   * \param node 节点
   * \return 下一个节点
   */
  WheelNode *next_node(WheelNode *node);
};

TimerList::TimerList(TimerWheel *timer_wheel) {
  auto *mem = kratos::service::box_malloc(sizeof(WheelNode));
  head = new (mem) WheelNode();
  head->next = head;
  head->prev = head;
  head->list = this;
  count = 0;
  wheel = timer_wheel;
}

TimerList::~TimerList() {
  WheelNode *node = first_node();
  while (node) {
    auto *temp = next_node(node);
    node->~WheelNode();
    kratos::service::box_free(node);
    node = temp;
  }
  if (head) {
    head->~WheelNode();
    kratos::service::box_free(head);
  }
}

void TimerList::add_front(WheelNode *node) {
  head->next->prev = node;
  node->prev = head;
  node->next = head->next;
  head->next = node;
  node->list = this;
  count += 1;
}

void TimerList::add_tail(WheelNode *node) {
  head->prev->next = node;
  node->next = head;
  node->prev = head->prev;
  head->prev = node;
  node->list = this;
  count += 1;
}

WheelNode *TimerList::remove(WheelNode *node) {
  if (node == head) {
    return nullptr;
  }
  if (node->prev) {
    node->prev->next = node->next;
  }
  if (node->next) {
    node->next->prev = node->prev;
  }
  node->prev = nullptr;
  node->next = nullptr;
  node->list = nullptr;
  count -= 1;
  return node;
}

void TimerList::destroy(WheelNode *node) {
  remove(node);
  node->~WheelNode();
  kratos::service::box_free(node);
}

WheelNode *TimerList::first_node() {
  if (head->next == head) {
    return nullptr;
  }
  return head->next;
}

WheelNode *TimerList::next_node(WheelNode *node) {
  if (node->next == head) {
    return nullptr;
  }
  return node->next;
}

TimerWheel::TimerWheel(kratos::service::ServiceBox *box) {
  box_ = box;
  for (std::size_t i = 0; i < SLOTS; i++) {
    auto *mem = kratos::service::box_malloc(sizeof(TimerList));
    auto *list = new (mem) TimerList(this);
    timer_slot_.push_back(list);
  }
}

TimerWheel::~TimerWheel() {
  for (auto &list : timer_slot_) {
    list->~TimerList();
    kratos::service::box_free(list);
  }
}

void TimerWheel::worker_update(std::time_t now) {
  if (!last_tick_) {
    last_tick_ = now;
  }
  if (last_tick_ == now) {
    worker_update_slot(now);
  } else {
    // 计算需要遍历的槽位
    std::time_t count = (now - last_tick_) / RESOLUTION;
    for (std::time_t i = 0; i < count; i++) {
      worker_update_slot(now);
    }
  }
  last_tick_ = now;
}

void TimerWheel::worker_update_slot(std::time_t now) {
  auto *timer_list = timer_slot_[worker_cur_slot_];
  auto *node = timer_list->first_node();
  while (node) {
    auto *temp = timer_list->next_node(node);
    if (node->deadline <= now) {
      auto result = false;
       if (!node->cancelled && node->handler) {
        result = node->handler(node, node->data);
      }
      if (node->cancelled) {
        destroy(node);
      } else if (node->is_repeated) {
        if (!result) {
          destroy(node);
        } else {
          // 超时，向后面调整
          timer_list->remove(node);
          node->deadline = now + node->duration;
          worker_add(node, now);
        }
      } else {
        destroy(node);
      }
    }
    node = temp;
  }
  worker_cur_slot_ = (worker_cur_slot_ + 1) % SLOTS;
}

void TimerWheel::worker_add(TimerID id, std::time_t now) {
  // 计算需要放入的槽位
  auto slot = (worker_cur_slot_ + (id->deadline - now) / RESOLUTION) % SLOTS;
  auto *timer_list = timer_slot_[slot];
  timer_list->add_front(id);
}

void TimerWheel::update(std::time_t now) {
  try {
    worker_update(now);
  } catch (std::exception &e) {
    if (box_) {
      box_->write_log(lang::LangID::LANG_RUNTIME_EXCEPTION,
                      klogger::Logger::FAILURE, typeid(e).name(), e.what());
    }
  }
}

TimerID TimerWheel::schedule(Handler handler, std::time_t duration,
                             std::uint64_t user_data) {
  if (!duration || !handler) {
    return nullptr;
  }
  auto now = kratos::util::get_os_time_millionsecond();
  auto deadline = now + duration;
  auto *mem = kratos::service::box_malloc(sizeof(WheelNode));
  auto *node = new (mem) WheelNode();
  node->handler = handler;
  node->duration = duration;
  node->deadline = deadline;
  node->data = user_data;
  node->is_repeated = true;
  worker_add(node, now);
  global_wheel_timer_count += 1;
  return node;
}

TimerID TimerWheel::schedule_once(Handler handler, std::time_t duration,
                                  std::uint64_t user_data) {
  if (!duration || !handler) {
    return nullptr;
  }
  auto now = kratos::util::get_os_time_millionsecond();
  auto deadline = now + duration;
  auto *mem = kratos::service::box_malloc(sizeof(WheelNode));
  auto *node = new (mem) WheelNode();
  node->handler = handler;
  node->duration = duration;
  node->deadline = deadline;
  node->data = user_data;
  node->is_repeated = false;
  worker_add(node, now);
  global_wheel_timer_count += 1;
  return node;
}

void TimerWheel::cancel(TimerID id) { id->cancelled = true; }

void TimerWheel::destroy(TimerID id) {
  if (!id) {
    return;
  }
  global_wheel_timer_count -= 1;
  if (id->list) {
    id->list->destroy(id);
  } else {
    id->~WheelNode();
    kratos::service::box_free(id);
  }
}

std::size_t get_wheel_timer_count() { return global_wheel_timer_count; }

} // namespace util
} // namespace kratos
